/** @file
	@brief Definitions related to #PrynEditorBoard. */
#ifndef PRYN_EDITOR_BOARD_H
#define PRYN_EDITOR_BOARD_H

typedef struct PrynEditorBoard PrynEditorBoard; ///< A 2D plane on which components can be built.
typedef enum PrynEditorBoardAction PrynEditorBoardAction; ///< What current action is being performed on the board.

#include <pryn/gui/control.h>
#include <pryn/gui/painter.h>

#include <pryn/editor/component.h>
#include <pryn/editor/path.h>

#include <pryn/component.h>
#include <pryn/state.h>

/** @defgroup PrynEditorBoard PrynEditorBoard C API
	@{ */

PrynEditorImport (PrynResult, prynEditorBoardCreate, (PrynEditorBoard **result, PrynState *state, const PrynString *title))
	/**< @brief Create an editor board that runs in its own top-level window.

	@param[out] result The resultant editor board.
	@param state The state to use for common data.
	@param title The initial title of the window created.
	@returns #PrynResultSuccess on success, or an error on failure; see #PrynResult. */

PrynEditorImport (PrynResult, prynEditorBoardCreateFromControl, (PrynEditorBoard **result, PrynGuiControl *control))
	/**< @brief Create an editor board to run within the control.

	@param[out] result The resultant editor board.
	@param control The control to use. This will be installed with an array of event handlers in painting, mouse control, etc. It does not need to be a top-level window.
	@returns #PrynResultSuccess on success, or an error on failure; see #PrynResult. */

PrynEditorImport (PrynResult, prynEditorBoardCreateFromHwnd, (PrynEditorBoard **result, PrynState *state, void *window))
	/**< @brief Create an editor board that runs within a Microsoft Windows control.

	@param[out] result The resultant editor board.
	@param state The state to use for common data.
	@param window The HWND to adopt. This will convert it into a control, which overloads the window procedure. It does not need to be a top-level window.
	@returns #PrynResultSuccess on success, or an error on failure; see #PrynResult. */

PrynEditorImport (PrynResult, prynEditorBoardPaint, (PrynEditorBoard *board, PrynGuiPainter *painter))
	/**< Paint the editor board.

	@param board The board to paint.
	@param painter The painter to use. All painter state will be destroyed and their initial states are irrelevant; fonts, pens, brushes, clipping regions, offsets, etc.
	@returns #PrynResultSuccess on success, or an error on failure; see #PrynResult. */

PrynEditorImport (PrynResult, prynEditorBoardShuffle, (PrynEditorBoard *board, int iterations, int percentEach))
	/**< @brief Push any intersecting components away from one another.

	@param board The board to push the components of.
	@param iterations The number of times to perform the pushes.
	@param percentEach How far in percentage to push overlapping components away.
	@returns #PrynResultSuccess if there are no longer any overlapping components, #PrynResultDone if there are. Errors may be returned on failure; see #PrynResult. */

PrynEditorImport (PrynResult, prynEditorBoardDirtyConnections, (PrynEditorBoard *board))
	/**< @brief Dirty all the connections.

	This sets a dirty bit on the editor board to indicate that the pathing, if present, is out of date and needs to be remade. That task is done by calling #prynEditorBoardUpdateConnections, preferably immediately before the paths are used to minimise the number of calculations done.

	@param board The board to mark as dirty.
	@returns #PrynResultSuccess if the board has been marked dirty, #PrynResultDone if the board was already marked dirty. Errors may be returned on failure; see #PrynResult. */

PrynEditorImport (PrynResult, prynEditorBoardUpdateConnections, (PrynEditorBoard *board))
	/**< @brief Recalculate any connections that are needed.

	If the connections haven't been marked dirty (if no components have been created or moved, and no pins have been connected or disconnected) then this has no effect and it returns #PrynResultDone. Otherwise this recalculates the board's path grid and the connections between them.

	@param board The board to update the connections of.
	@returns #PrynResultSuccess if an update was required and completed successfully, #PrynResultDone if no update was required. Errors may be returned on failure, see #PrynResult.
	*/

PrynEditorImport (PrynResult, prynEditorBoardPickComponent, (PrynEditorBoard *board, int x, int y, PrynEditorComponent **editorComponent))
	/**< Pick which component this position on the board (not adjusted for scroll) is over.

	If there is no component, @e editorComponent is set to zero and #PrynResultDone is returned.
		
	@param board The board to search.
	@param x Horizontal position of the point to test. This is relative to the current scroll point.
	@param y Vertical position of the point to test. This is relative to the current scroll point.
	@param[out] editorComponent Written with the component the position is over, or 0 if there is no component at this position. This is an optional parameter.
	@returns #PrynResultSuccess if a matching component was found, #PrynResultDone if no match was made. Errors may be returned on failure; see #PrynResult. */

PrynEditorImport (PrynResult, prynEditorBoardPickComponentOrPin, (PrynEditorBoard *board, int x, int y, PrynEditorComponent **editorComponent, PrynPin **pin)) 
	/**< @brief Pick which pin this position on the board (not adjusted for scroll) is over.
	
	If there is no component or pin, @e editorComponent and @e pin are set to zero and #PrynResultDone is returned. If it's over a pin, @e editorComponent is set to the component which the @e pin is in. Otherwise @e pin remains zero and @e editorComponent is set to the component.

	@param board The board to search.
	@param x Horizontal position of the point to test. This is relative to the current scroll point.
	@param y Vertical position of the point to test. This is relative to the current scroll point.
	@param[out] editorComponent Written with the component the position is over, or 0 if there is no component at this position. This is an optional parameter.
	@param[out] pin Written with the pin the position is over, or 0 if there is no pin at this position. This is an optional parameter.
	@returns #PrynResultSuccess if a matching pin or component was found, #PrynResultDone if no match was made. Errors may be returned on failure, see #PrynResult. */

PrynEditorImport (PrynResult, prynEditorBoardFirstOverlappingComponent, (PrynEditorBoard *board, PrynEditorComponent **component, const PrynRectangle *area))
	/**< @brief Find the first editor component that overlaps the area.

	@param board The board to search.
	@param[out] component Written with the component on success, or 0 on failure (including error).
	@param area Area to search, relative to the current scroll point.
	@returns #PrynResultSuccess if a component overlapping the area was found, or #PrynResultDone. Errors may be returned on failure, see #PrynResult. */

PrynEditorImport (PrynResult, prynEditorBoardNextOverlappingComponent, (PrynEditorComponent *reference, PrynEditorComponent **component, const PrynRectangle *area))
	/**< @brief Find the next editor component that overlaps this area.

	@param reference The last matching component.
	@param[out] component Written with the next matching component on success, or 0 on failure (including error).
	@param area The area to search, relative to the current scroll point.
	@returns #PrynResultSuccess if a component overlapping the area was found, or #PrynResultDone if iteration is complete. Errors may be returned on failure, see #PrynResult. */

PrynEditorImport (PrynResult, prynEditorBoardIterateOverlappingComponents, (PrynEditorBoard *board, PrynEditorComponent **component, const PrynRectangle *area))
	/**< @brief Iterate over the editor components that overlap the area.

	This merges #prynEditorBoardFirstOverlappingComponent and #prynEditorBoardNextOverlappingComponent. If *component is zero, this starts iteration; otherwise it continues it. Note that it's important to terminate iteration if #PrynResultDone is returned, or else the iteration will start over again.

	@param board The board to search.
	@param[in, out] component Read as the last matching component if the object pointed to is non-zero, or as an indicator that iteration needs to be started if zero. This is written with the next matching component on success or 0 on failure (including error).
	@param area The area to search, relative to the current scroll point.
	@returns #PrynResultSuccess if a component overlapping the area was found, or #PrynResultDone if iteration is complete. Errors may be returned on failure, see #PrynResult. */

/// What current action is being performed on the board.
enum PrynEditorBoardAction
{
	PrynEditorBoardActionNone, ///< Nothing is happening.
	PrynEditorBoardActionClick, ///< The mouse has been pushed down on a component or pin but no dragging has occurred.
	PrynEditorBoardActionDraggingComponent, ///< The over editor component is being dragged around by the mouse.
	PrynEditorBoardActionDraggingPin, ///< The overPin is being dragged around by the mouse.
	PrynEditorBoardActionDraggingBoard, ///< The board is being dragged by the mouse, scrolling it.
};

#ifdef PrynEditorInternalStructs
/** A 2D plane on which components can be built. This also contains information for editing the board.

	@ingroup PrynEditorBoard
*/
struct PrynEditorBoard
{
#ifdef PrynEditorInternal
/** @name Internal fields
	These fields are not normally visible to clients, and it is recommended not to use them to maximise binary compatibility. To make them available, define PrynEditorInternal.
	@{ */
	PrynState *state; ///< Pryn state from which components can be made.
	PrynEditorComponentList components; ///< Linked list of editor components.
	PrynEditorComponent *over; ///< What component the mouse is over or 0 for none.
	PrynPin *overPin; ///< What pin the mouse is over or 0 for none.
	
	PrynEditorComponent *target; ///< What component the mouse is over while dragging.
	PrynPin *targetPin; ///< What pin the mouse is over while dragging pins.
	
	PrynEditorBoardAction action; ///< What action is being performed.
	int mouseX, mouseY; ///< Current mouse position on the board relative to scroll position.
	int dragStartX, dragStartY; ///< Board mouse position where a drag action starts.
	int scrollX, scrollY; ///< Current scroll position.
	void *platform; ///< Platform-specific data for the board.
	
	PrynGuiControl *control; ///< Control we're using.
	
	PrynEditorPath path; ///< Pathing lattice.
	bool pathDirty; ///< If true, the path needs to be remade.
	
	int ticks; ///< Incremented 20 times a second or so while editing.
/** @} */
#endif /* PrynEditorInternal */

#if __cplusplus
	inline static PrynEditorBoard *Create (PrynState *state, const PrynString &title) { PrynEditorBoard *result; prynEditorBoardCreate (&result, state, &title); return result; }
		/**< @brief Create an editor board that runs in its own top-level window.

		@param state The state to use for common data.
		@param title The initial title of the window created.
		@returns The newly created board. */

	inline static PrynEditorBoard *CreateFromControl (PrynState *state, PrynGuiControl *control) { PrynEditorBoard *result; prynEditorBoardCreateFromControl (&result, control); return result; }
		/**< @brief Create an editor board to run within the control.

		@param state The state to use for common data.
		@param control The control to use. This will be installed with an array of event handlers in painting, mouse control, etc. It does not need to be a top-level window. 
		@returns The newly created board. */

	inline static PrynEditorBoard *CreateFromHwnd (PrynState *state, void *window) { PrynEditorBoard *result; prynEditorBoardCreateFromHwnd (&result, state, window); return result; }
		/**< @brief Create an editor board that runs within a Microsoft Windows control.

		@param state The state to use for common data.
		@param window The HWND to adopt. This will convert it into a control, which overloads the window procedure. It does not need to be a top-level window.
		@returns The newly created board. */

	inline void paint (PrynGuiPainter *painter) { prynEditorBoardPaint (this, painter); }
		/**< Paint the editor board.

		@param painter The painter to use. All painter state will be destroyed and their initial states are irrelevant; fonts, pens, brushes, clipping regions, offsets, etc. */

	inline bool shuffle (int iterations, int percentEach) { PrynResult result = prynEditorBoardShuffle (this, iterations, percentEach); return result == PrynResultSuccess; }
		/**< @brief Push any intersecting components away from one another.

			@param iterations The number of times to perform the pushes.
			@param percentEach How far in percentage to push overlapping components away.
			@returns @b true if there are no longer any overlapping components, @b false if there are. */

	inline bool dirtyConnections () { PrynResult result = prynEditorBoardDirtyConnections (this); return result == PrynResultSuccess; }
		/**< @brief Dirty all the connections.

			This sets a dirty bit on the editor board to indicate that the pathing, if present, is out of date and needs to be remade. That task is done by calling #updateConnections, preferably immediately before the paths are used to minimise the number of calculations done.

		@returns @b true if the board has been marked dirty, @b false if the board was already marked dirty. */

	inline bool updateConnections () { PrynResult result = prynEditorBoardUpdateConnections (this); return result == PrynResultSuccess; }
		/**< @brief Recalculate any connections that are needed.

		If the connections haven't been marked dirty (if no components have been created or moved, and no pins have been connected or disconnected) then this has no effect and it returns #PrynResultDone. Otherwise this recalculates the board's path grid and the connections between them.

		@returns @b true if an update was required and completed successfully, @b false if no update was required. */

	inline PrynEditorComponent *pickComponent (int x, int y) { PrynEditorComponent *editor; PrynResult result = prynEditorBoardPickComponent (this, x, y, &editor); return editor; }
		/**< Pick which component this position on the board (not adjusted for scroll) is over.

		If there is no component, @e editorComponent is set to zero and #PrynResultDone is returned.
			
		@param x Horizontal position of the point to test. This is relative to the current scroll point.
		@param y Vertical position of the point to test. This is relative to the current scroll point.
		@returns The editor component if one was found, or 0 if none was found. */

	inline bool pickComponentOrPin (int x, int y, PrynEditorComponent **editorComponent, PrynPin **pin) { PrynResult result = prynEditorBoardPickComponentOrPin (this, x, y, editorComponent, pin); return result == PrynResultSuccess; }
		/**< @brief Pick which pin this position on the board (relative to the current scroll point) is over.
		
		If there is no component or pin, @e editorComponent and @e pin are set to zero and @b false is returned. If it's over a pin, @e editorComponent is set to the component which the @e pin is in. Otherwise @e pin remains zero and @e editorComponent is set to the component.

		@param[in] x Horizontal position of the point to test. This is relative to the current scroll point.
		@param[in] y Vertical position of the point to test. This is relative to the current scroll point.
		@param[out] editorComponent Written with the component the position is over, or 0 if there is no component at this position. This is an optional parameter.
		@param[out] pin Written with the pin the position is over, or 0 if there is no pin at this position. This is an optional parameter.
		@returns @b false if the point is over no pins or components, @b true if it's over a pin or a component. */

	inline PrynEditorComponent *firstOverlapping (const PrynRectangle &area) { PrynEditorComponent *component; prynEditorBoardFirstOverlappingComponent (this, &component, &area); return component; }
		/**< @brief Find the first editor component that overlaps the area.

		@param area Area to search, relative to the current scroll point.
		@returns The first matching component in this area, or 0 if none are found. */

	inline PrynEditorComponent *nextOverlapping (PrynEditorComponent *reference, const PrynRectangle &area) { PrynEditorComponent *component; prynEditorBoardNextOverlappingComponent (reference, &component, &area); return component; }
		/**< @brief Find the next editor component that overlaps this area.

		@param reference The last matching component.
		@param area The area to search, relative to the current scroll point.
		@returns The next matching component in this area, or 0 if none are found. */

	inline bool iterateOverlapping (PrynEditorComponent **component, const PrynRectangle &area) { return prynEditorBoardIterateOverlappingComponents (this, component, &area) == PrynResultSuccess; }
		/**< @brief Iterate over the editor components that overlap the area.

		This merges #firstOverlapping and #nextOverlapping. If *component is zero, this starts iteration; otherwise it continues it. Note that it's important to terminate iteration if @b false is returned, or else the iteration will start over again.

		@param[in, out] component Read as the last matching component if the object pointed to is non-zero, or as an indicator that iteration needs to be started if zero. This is written with the next matching component on success or 0 on failure (including error).
		@param area The area to search, relative to the current scroll point.
		@returns @b true if a matching component was found, @b false to terminate iteration. */

#endif /* __cplusplus */
};
#endif /* PrynEditorStructs */

/** @} */ // @addtogroup PrynEditorBoard

#endif /* PRYN_EDITOR_BOARD_H */
